#include "eeprom.h"

i2c_handle_type hi2cx;

/**
  * @brief  initializes peripherals used by the i2c.
  * @param  none
  * @retval none
  */
void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
  gpio_init_type gpio_initstructure;

  if(hi2c->i2cx == I2Cx_PORT)
  {
    /* i2c periph clock enable */
    crm_periph_clock_enable(I2Cx_CLK, TRUE);
    crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);
    crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);

    /* gpio configuration */
    gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_initstructure.gpio_pull           = GPIO_PULL_UP;
    gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;

    /* configure i2c pins: scl */
    gpio_initstructure.gpio_pins = I2Cx_SCL_PIN;
    gpio_init(I2Cx_SCL_GPIO_PORT, &gpio_initstructure);
    gpio_pin_mux_config(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_PIN_SOURCE, I2Cx_SCL_PIN_MUX_NUM);

    /* configure i2c pins: sda */
    gpio_initstructure.gpio_pins = I2Cx_SDA_PIN;
    gpio_init(I2Cx_SDA_GPIO_PORT, &gpio_initstructure);
    gpio_pin_mux_config(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_PIN_SOURCE, I2Cx_SDA_PIN_MUX_NUM);

    /* configure and enable i2c dma channel interrupt */
    nvic_irq_enable(I2Cx_DMA_TX_IRQn, 0, 0);
    nvic_irq_enable(I2Cx_DMA_RX_IRQn, 0, 0);

    /* configure and enable i2c interrupt */
    nvic_irq_enable(I2Cx_EVT_IRQn, 0, 0);
    nvic_irq_enable(I2Cx_ERR_IRQn, 0, 0);

    /* i2c dma tx and rx channels configuration */
    /* enable the dma clock */
    crm_periph_clock_enable(I2Cx_DMA_CLK, TRUE);

    /* i2c dma channel configuration */
    dma_reset(hi2c->dma_tx_channel);
    dma_reset(hi2c->dma_rx_channel);

    hi2c->dma_tx_channel = I2Cx_DMA_TX_CHANNEL;
    hi2c->dma_rx_channel = I2Cx_DMA_RX_CHANNEL;

    dma_default_para_init(&hi2c->dma_init_struct);
    hi2c->dma_init_struct.peripheral_inc_enable    = FALSE;
    hi2c->dma_init_struct.memory_inc_enable        = TRUE;
    hi2c->dma_init_struct.peripheral_data_width    = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
    hi2c->dma_init_struct.memory_data_width        = DMA_MEMORY_DATA_WIDTH_BYTE;
    hi2c->dma_init_struct.loop_mode_enable         = FALSE;
    hi2c->dma_init_struct.priority                 = DMA_PRIORITY_LOW;
    hi2c->dma_init_struct.direction                = DMA_DIR_MEMORY_TO_PERIPHERAL;

    dma_init(hi2c->dma_tx_channel, &hi2c->dma_init_struct);
    dma_init(hi2c->dma_rx_channel, &hi2c->dma_init_struct);

    i2c_init(hi2c->i2cx, I2C_FSMODE_DUTY_2_1, I2Cx_SPEED);

    i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, I2Cx_ADDRESS);
  }
}

void eeprom_i2c_init(void)
{
  hi2cx.i2cx = I2Cx_PORT;
  i2c_config(&hi2cx);
}

static void error_handler(uint32_t error_code)
{
  while(1)
  {
    gpio_bits_write(GPIOA ,GPIO_PINS_15, !gpio_output_data_bit_read(GPIOA, GPIO_PINS_15));
    delay_ms(500);
  }
}

void eeprom_read_page(uint8_t addr, uint8_t *rx_buf)
{
	i2c_status_type i2c_status;
	/* start the request reception process */
	if((i2c_status = i2c_master_transmit_dma(&hi2cx, EEPROM_ADDR, &addr, 1, I2C_TIMEOUT)) != I2C_OK)
	{
		error_handler(i2c_status);
	}

	/* wait for the communication to end */
	if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)
	{
		error_handler(i2c_status);
	}
	/* start the request reception process */
	if((i2c_status = i2c_master_receive_dma(&hi2cx, EEPROM_ADDR, rx_buf, 8, I2C_TIMEOUT)) != I2C_OK)
	{
		error_handler(i2c_status);
	}

	/* wait for the communication to end */
	if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)
	{
		error_handler(i2c_status);
	}
}

void eeprom_write_page(uint8_t *data)
{
	i2c_status_type i2c_status;
	/* start the request reception process */
	if((i2c_status = i2c_master_transmit_dma(&hi2cx, EEPROM_ADDR, data, 9, I2C_TIMEOUT)) != I2C_OK)
	{
		error_handler(i2c_status);
	}

	/* wait for the communication to end */
	if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)
	{
		error_handler(i2c_status);
	}
}

void eeprom_write_int16(uint8_t addr, int16_t value)
{
	uint8_t write_data[9] = {0};
	write_data[0] = addr;
	write_data[1] = value & 0xff;
	write_data[2] = value >> 8 & 0xff;
	write_data[3] = 0xff;
	write_data[4] = 0xff;
	write_data[5] = 0xff;
	write_data[6] = 0xff;
	write_data[7] = 0xff;
	write_data[8] = 0xff;
	
	eeprom_write_page(write_data);
}

void eeprom_read_int16(uint8_t addr, int16_t *value)
{
	uint8_t read_buf[8] = {0};
	eeprom_read_page(addr, read_buf);
	*value = (int16_t)(read_buf[1] << 8 | read_buf[0]);
}